<?php
// +----------------------------------------------------------------------
// | FileName: DrawingService.php
// +----------------------------------------------------------------------
// | Date: 2023/4/2 21:08:45
// +----------------------------------------------------------------------
// | Author: 默毅 <moyi@mymoyi.cn>
// +----------------------------------------------------------------------
// | Notes: 
// +----------------------------------------------------------------------
	
	namespace App\Service\Library;
	
	use App\Model\Ai\Atlas\Atlas;
	use App\Model\Ai\Atlas\AtlasList;
	use App\Model\Ai\Atlas\AtlasTag;
	use App\Model\Ai\Drawing\DrawingLora;
	use App\Model\Ai\Drawing\DrawingModel;
	use App\Model\Ai\Drawing\DrawingSampler;
	use App\Model\Ai\Drawing\DrawingServer;
	use App\Model\Ai\Drawing\DrawingTask;
	use App\Model\Ai\Drawing\DrawingVae;
	use App\Model\Ai\Drawing\Link\LinkLora;
	use App\Model\Ai\Drawing\Link\LinkModel;
	use App\Model\Ai\Drawing\Link\LinkSampler;
	use App\Model\Ai\Drawing\Link\LinkVae;
	use App\Model\Ai\Picture;
	use App\Model\Ai\Tag\TagAesthetic;
	use App\Model\Ai\Tag\TagCheck;
	use App\Model\Ai\Tag\TagClip;
	use App\Model\Ai\Tag\TagDeepdanbooru;
	use App\Model\Ai\Tag\TagNsfw;
	use App\Model\Ai\Tag\TagStyle;
	use Hyperf\Context\Context;
	use Hyperf\Database\Exception\QueryException;
	use Hyperf\DbConnection\Db;
	use Hyperf\Di\Annotation\Inject;
	use Hyperf\HttpServer\Contract\RequestInterface;
	
	class DrawingService
	{
		#[Inject]
		protected CommonService $CommonService;
		
		function getLimit(){
			return json_decode('{"enable_hr":{"title":"Enable Hr","desc":"高清分辨率修复","type":"boolean","default":false},"denoising_strength":{"title":"Denoising Strength","desc":"重绘强度","max":1,"min":0,"type":"decimal","default":0},"firstphase_width":{"title":"Firstphase Width","desc":"一阶宽度（不知道干啥的）","type":"integer","default":0},"firstphase_height":{"title":"Firstphase Height","desc":"一阶高度 （不知道干啥的）","type":"integer","default":0},"hr_scale":{"title":"Hr Scale","desc":"放大倍率","max":4,"min":1,"type":"decimal","default":2},"hr_upscaler":{"title":"Hr Upscaler","desc":"高清化算法","type":"option","default":"Latent"},"hr_second_pass_steps":{"title":"Hr Second Pass Steps","desc":"采样步数","type":"integer","default":0,"max":150,"min":0},"hr_resize_x":{"desc":"将宽度调整到","title":"Hr Resize X","type":"integer","max":2048,"default":0},"hr_resize_y":{"desc":"将高度调整到","title":"Hr Resize Y","type":"integer","max":2048,"default":0},"prompt":{"title":"Prompt","desc":"提示词","type":"string","default":"1girl"},"styles":{"title":"Styles","type":"option","desc":"模板预设","items":{"type":"string"}},"seed":{"title":"Seed","desc":"图像生成种子","type":"integer","default":-1},"subseed":{"title":"Subseed","desc":"随机数差异种子","type":"integer","default":-1},"subseed_strength":{"title":"Subseed Strength","desc":"差异强度","type":"number","default":0},"seed_resize_from_h":{"title":"Seed Resize From H","desc":"种子随机数高度","type":"integer","default":-1},"seed_resize_from_w":{"title":"Seed Resize From W","desc":"种子随机数宽度","type":"integer","default":-1},"sampler_name":{"title":"Sampler Name","desc":"采样器","type":"option","default":"Euler a"},"batch_size":{"title":"Batch Size","desc":"每批数量","type":"integer","default":1,"max":100,"min":1},"n_iter":{"title":"N Iter","desc":"N Iter（不知道干啥的）","type":"integer","default":1},"steps":{"title":"Steps","desc":"迭代步数","type":"integer","default":20,"max":150,"min":1},"cfg_scale":{"title":"Cfg Scale","desc":"提示词引导系数","type":"number","default":7,"max":30,"min":1},"width":{"title":"Width","desc":"宽度","type":"integer","default":368,"max":2048,"min":64},"height":{"title":"Height","desc":"高度","type":"integer","default":368,"max":2048,"min":64},"restore_faces":{"title":"Restore Faces","desc":"面部修复","type":"boolean","default":false},"tiling":{"title":"Tiling","desc":"无缝贴图","type":"boolean","default":false},"do_not_save_samples":{"title":"Do Not Save Samples","desc":"不保存采样器（不知道干啥的）","type":"boolean","default":false},"do_not_save_grid":{"title":"Do Not Save Grid","desc":"不保存网格（不知道干啥的）","type":"boolean","default":false},"negative_prompt":{"title":"Negative Prompt","desc":"反向提示词","type":"string"},"eta":{"title":"Eta","desc":"Eta 噪声（不知道干啥的）","type":"number"},"s_churn":{"title":"S Churn","desc":"sigma混乱强度","type":"number","default":0},"s_tmax":{"title":"S Tmax","desc":"sigma上限值","type":"number"},"s_tmin":{"title":"S Tmin","desc":"sigma下限值","type":"number","default":0},"s_noise":{"title":"S Noise","desc":"sigma噪声强度","type":"number","default":1},"override_settings":{"title":"Override Settings","desc":"覆盖设置","type":"object"},"override_settings_restore_afterwards":{"title":"Override Settings Restore Afterwards","desc":"覆盖设置之后恢复","type":"boolean","default":true},"script_args":{"title":"Script Args","desc":"脚本参数","type":"array","items":[],"default":[]},"sampler_index":{"title":"Sampler Index","desc":"采样器索引index","type":"string","default":"Euler"},"script_name":{"title":"Script Name","desc":"脚本名称","type":"string"},"send_images":{"title":"Send Images","desc":"发送图片","type":"boolean","default":true},"save_images":{"title":"Save Images","desc":"保存图片","type":"boolean","default":true},"alwayson_scripts":{"title":"Alwayson Scripts","desc":"Alwayson脚本","type":"object","default":[]},"controlnet_units":{"title":"Controlnet Units","desc":"控制单元","type":"controlnet","items":{"ref":"#\/components\/schemas\/ControlNetUnitRequest"},"description":"ControlNet Processing Units","default":[{"input_image":"","mask":"","module":"none","model":"None","weight":1,"resize_mode":"Scale to Fit (Inner Fit)","lowvram":false,"processor_res":64,"threshold_a":64,"threshold_b":64,"guidance":1,"guidance_start":0,"guidance_end":1,"guessmode":true}],"docs_default":[{"input_image":"","mask":"","module":"none","model":"None","weight":1,"resize_mode":"Scale to Fit (Inner Fit)","lowvram":false,"processor_res":64,"threshold_a":64,"threshold_b":64,"guidance":1,"guidance_start":0,"guidance_end":1,"guessmode":true}]}}', true);
		}
		function joinServer($data)
		{
			try {
				
				print_r(['$data'=>$data]);
				$time = time();
				// 系统配置
				$system_param = [
					'gpu_serial' => $data['gpu']['serial'],
					'gpu_name' => $data['gpu']['name'],
					'gpu_ram' => $data['cuda']['total'],
					'ram' => $data['ram']['total']
				];
				print_r(['$system_param' => $system_param]);
				$server = DrawingServer::where('gpu_serial', $system_param['gpu_serial'])->first();
				if ($server) {
					$server_id = $server['id'];
					DrawingServer::where('id', $server_id)->update($system_param);
				} else {
					$server = DrawingServer::forceCreate([...$system_param, ...['status' => 'normal']]);
					print_r(['新的服务器加入' => $server['id']]);
				}
				$server_id = $server['id'];
				$this->updateModels($data['models'],$server_id);
				$this->updateModels($data['vae_list'],$server_id,'vae');
				$this->updateModels($data['loras_list'],$server_id,'lora');
				$this->updateModels($data['samplers'],$server_id,'sampler');
				
				return $server_id;
			} catch (\Exception $e) {
				return 0;
			}
		}
		
		function updateVae($list,$server_id){
		
		}
		
		function updateSampler($list,$server_id){
			$models_name_ids = [];
			$models_name = [];
			$time = time();
			$Link = new LinkSampler();
			foreach ($list as $item) {
				if(!$item['sha256']){
					continue;
				}
				$models_name_ids[] = $item['name'];
				$models_name[$item['name']] = [...$item, ...['server_id' => $server_id, 'createtime' => $time]];
			}
			
			// 查询当前服务器的Model列表
			$ModelLinkName = $Link->where('server_id', $server_id)->pluck('name');;
			$models_link_name_ids = $ModelLinkName->toArray();
			
			
			// 不存在的差集关系数据存档
			$diff = array_diff($models_name_ids, $models_link_name_ids);
			if ($diff) {
				$param = [];
				foreach ($diff as $name) {
					$param[] = $models_name[$name];
				}
				$Link->insert($param);
			}
			
			
			// 服务器移除的模型的差集关系数据修改
			$diff = array_diff($models_link_name_ids, $models_name_ids);
			if ($diff) {
				// 移除模型关联
				$Link->whereIn('name', $diff)->where('server_id', $server_id)->delete();
			}
			
		}
		
		// 更新模型
		function updateModels($list,$server_id,$type = 'models'){
			$models_sha256_ids = [];
			$models_sha256 = [];
			$models_name_ids = [];
			$models_name = [];
			$time = time();
			$sampler = false;
			switch ($type){
				case 'lora':
					$Link = new LinkLora();
					$Model = new DrawingLora();
					break;
				case 'vae':
					$Link = new LinkVae();
					$Model = new DrawingVae();
					break;
				case 'sampler':
					$sampler = true;
					$Link = new LinkSampler();
					$Model = new DrawingSampler();
					break;
				default:
					$Link = new LinkModel();
					$Model = new DrawingModel();
					break;
			}
			
			
			if($sampler){
				foreach ($list as $item) {
					$models_name_ids[] = $item['name'];
					$models_name[$item['name']] = [...$item, ...['server_id' => $server_id, 'createtime' => $time]];

				}
			}else{
				foreach ($list as $item) {
					if(!$item['sha256']){
						continue;
					}
					$models_sha256_ids[] = $item['sha256'];
					$models_name_ids[] = $item['name'];
					$models_name[$item['name']] = [...$item, ...['server_id' => $server_id, 'createtime' => $time]];
					unset($item['title']);
					$models_sha256[$item['sha256']] = [...$item, ...['server_id' => $server_id, 'createtime' => $time]];
				}
			}
			
			
			// 查询当前服务器的Model列表
			$ModelLinkName = $Link->where('server_id', $server_id)->pluck('name');
			$models_link_name_ids = $ModelLinkName->toArray();

			// 不存在的差集关系数据存档
			$diff = array_diff($models_name_ids, $models_link_name_ids);
			if ($diff) {
				$param = [];
				foreach ($diff as $name) {
					$param[] = $models_name[$name];
				}
				$Link->insert($param);
			}
			
			
			// 服务器移除的模型的差集关系数据修改
			$diff = array_diff($models_link_name_ids, $models_name_ids);
			if ($diff) {
				// 移除模型关联
				$Link->whereIn('name', $diff)->where('server_id', $server_id)->delete();
			}
			
			
			if($sampler){
				// 查询当前服务器跟系统Model列表的差集
				$ModelNameList = $Model->whereIn('name', $models_name_ids)->pluck('name');
				$diff = array_diff($models_name_ids, $ModelNameList->toArray());
				if ($diff) {
					$param = [];
					foreach ($diff as $name) {
						$param[] = $models_name[$name];
					}
					$Model->insert($param);
				}
			}else{
				// 查询当前服务器跟系统Model列表的差集
				$ModelSha256List = $Model->whereIn('sha256', $models_sha256_ids)->pluck('sha256');
				$diff = array_diff($models_sha256_ids, $ModelSha256List->toArray());
				if ($diff) {
					$param = [];
					foreach ($diff as $sha256) {
						$param[] = $models_sha256[$sha256];
					}
					$Model->insert($param);
				}
			}
		}
		
		
		function filter_array_value_key(&$arr): void
		{
			foreach ($arr as $key => &$value) {
				if (is_array($value)) {
					// 递归过滤数组中的值为数组的键
					$this->filter_array_value_key($value);
				}
				if (is_numeric($key)) {
					// 移除值为数组的键
					unset($arr[$key]);
				}
			}
		}
		
		function build_tag(&$arr, $id): void
		{
			foreach ($arr as &$item) {
				if(!is_numeric($item['score'] )){
					$item['score'] = 1;
				}
				$item['createtime'] = time();
				$item['picture_id'] = $id;
			}
		}
		
		function getDrawingTask($service_id): bool|array
		{
			$task = DrawingTask::where('status', 'wait')->orderBy('weight', 'desc')->first();
			if (!$task) {
				print_r(['暂无任务']);
				return false;
			}
			$update = DrawingTask::where([['status', '=', 'wait'], ['id', '=', $task['id']]])->update(['status' => 'processing','service_id'=>$service_id]);
			if (!$update) {
				print_r(['锁定任务失败']);
				return false;
			}
			return $task->toArray();
		}
		
		function recoveryTask($task, $status = 'wait'): int
		{
			DrawingTask::where([['id', '=', $task['id']]])->limit(1)->update(['status' => $status]);
			return 1;
		}
		
		function recoveryServer($server, $usage = 'free'): int
		{
			return DrawingServer::where([['id', '=', $server['id']]])->limit(1)->update(['usage' => $usage]);
		}
		
		function getDrawingServer($task_id): bool|array
		{
			// 查询服务器
			$server = DrawingServer::where([['status', '=', 'normal'], ['usage', '=', 'free']])->first();
			if (!$server) {
				return false;
			}
			$server_id = $server['id'];
			$update = DrawingServer::where([['status', '=', 'normal'], ['usage', '=', 'free'], ['id', '=', $server_id]])->update(['usage' => 'processing', 'task_id' => $task_id]);
			if (!$update) {
				return false;
			}
			return $server->toArray();
		}
		
		function pushData($parameters, $default_atlas_id = null): bool
		{
			try {
				$atlas_id = $default_atlas_id;
				$server_id = @$parameters['server_id'];
				$task_id = @$parameters['task_id'];
				if (!@$server_id) {
					print_r(['不存在服务器ID']);
					return false;
				}
				if(!@$task_id){
					print_r(['不存在任务ID']);
					return false;
				}
				
				
				// 取服务器ID
				$task = DrawingTask::findFromCache($task_id);
				$server = DrawingServer::findFromCache($server_id);
				// 设置服务器空闲
				$this->recoveryServer($server);
				// 设置任务已完成
				$this->recoveryTask($task,'finish');
				
				$user_id = $task['user_id'];
				// 存在服务器ID的话 恢复服务器
				$time = time();
				if (@!$parameters['url']) {
					print_r(['不存在链接是返回']);
					// 失败存档
					$task->save(['status' => 'fail']);
					return false;
				}
				
				
				if (!$atlas_id) {
					$atlas_id = Atlas::insertGetId([
						'createtime' => time(),
						'user_id' => $user_id,
					]);
				}
				
//				print_r(['$data' => $parameters]);
				// 图片合集
				$atlas_list = [];
				$atlas_tag = [];
				$total = 0;
				// 图集封面ID，先存分，取最高
				$atlas_picture = [
					'id' => 0,
					'score' => 0
				];
//				print_r(['url' => $parameters['url']]);
				foreach ($parameters['url'] as $index => $url) {
					$clip = $parameters['clip'][$index];
					// 匹配带括号的权重数据部分
					preg_match_all('/\((.*?)\)/', $clip, $matches);
					// clip标签   ####
					$clip_tag = [];
					foreach ($matches[1] as $item) {
						list($label, $score) = explode(":", $item);
						$clip_tag[] = [
							'label' => $label,
							'score' => $score
						];
						$atlas_tag[$index][] = $label;
					}
					// 匹配自然语言部分
					preg_match('/^([^(]+)(.*)$/', $clip, $matches);
					// 自然语言     ####
					$clip = trim($matches[1]);
					preg_match_all('/\(([^():]+):([^()]+)\)/', $parameters['deepdanbooru'][$index], $matches, PREG_SET_ORDER);
					$deepdanbooru_tag = [];
					foreach ($matches as $match) {
						$label = str_replace(['(', ')', '[', ']'], '', $match[1]);
						$deepdanbooru_tag[] = [
							'label' => $label,
							'score' => (float)$match[2]
						];
						if ((float)$match[2] > 0.7) {
							$atlas_tag[$index][] = $label;
						}
					}
					$status = 'normal';
					
					// 阿里云增强审核标签      ####
					$check_tag = [];
					foreach (@$parameters['check']??[$index] as $item) {
						if (@$item['label'] != 'nonLabel') {
							if (@$item['label'] == 'sexual_affectionDisplay' || @$item['label'] == 'sexual_partialNudity') {
								$status = 'sexy';
							} else {
								$status = 'pending';
							}
						}
						$check_tag[] = [
							'score' => @$item['confidence'] ?? 1,
							'label' => @$item['label'] ?? 'unknown',
						];
					}
					
					$nsfw_tag = $parameters['nsfw'][$index];
					$aesthetic_tag = $parameters['aesthetic'][$index];
					$style_tag = $parameters['style'][$index];
					
					foreach ($parameters['style'][$index] as $item) {
						if ($item['score'] > 0.5) {
							$atlas_tag[$index][] = $item['label'];
						}
					}
					
					
					$metadata = $parameters;
					
					$this->filter_array_value_key($metadata);
					$metadata = json_encode($metadata);
					
					$param = [
						'user_id' => $user_id,
						'atlas_id' => $atlas_id,
						'task_id' =>$task_id,
						// 自然语言存储
						'clip' => $clip,
						'story' => @$parameters['story'][$index],
						'title' => @$parameters['title'][$index],
						'prompt' => @$parameters['all_prompts'][$index] ?? $parameters['prompt'],
						'negative_prompt' => @$parameters['all_negative_prompt'][$index] ?? $parameters['negative_prompt'],
						'width' => $parameters['width'],
						'height' => $parameters['height'],
						'model_hash' => $parameters['model_hash'],
						'model_name' => $parameters['model_name'],
						'sd_model_hash' => $parameters['sd_model_hash'],
						'sd_vae' => $parameters['sd_vae'],
						'sampler_name' => $parameters['sampler_name'],
						'steps' => $parameters['steps'],
						'cfg_scale' => $parameters['cfg_scale'],
						'score' => @$parameters['score'][$index],
						'url' => @$parameters['url'][$index],
						'seeds' => @$parameters['all_seeds'][$index] ?? $parameters['seed'],
						'subseeds' => @$parameters['all_subseeds'][$index] ?? $parameters['subseed'],
						'server_id' => $server_id,
						'metadata' => $metadata,
						'status' => $status,
						'createtime' => $time,
						'updatetime' => $time,
					];
					
					$picture_id = Picture::insertGetId($param);
					
					$this->build_tag($deepdanbooru_tag, $picture_id);
					TagDeepdanbooru::insert($deepdanbooru_tag);
					$this->build_tag($check_tag, $picture_id);
					TagCheck::insert($check_tag);
					$this->build_tag($clip_tag, $picture_id);
					TagClip::insert($clip_tag);
					$this->build_tag($nsfw_tag, $picture_id);
					TagNsfw::insert($nsfw_tag);
					$this->build_tag($aesthetic_tag, $picture_id);
					TagAesthetic::insert($aesthetic_tag);
					$this->build_tag($style_tag, $picture_id);
					TagStyle::insert($style_tag);
					
					if ($status != 'normal') {
						$status = 'hidden';
					} else {
						$total++;
						if ($param['score'] > $atlas_picture['score']) {
							$atlas_picture['score'] = $param['score'];
							$atlas_picture['id'] = $picture_id;
						}
					}
					
					$atlas_list[] = [
						'status' => $status,
						'atlas_id' => $atlas_id,
						'picture_id' => $picture_id,
					];
					print_r([$picture_id, count($clip_tag), count($deepdanbooru_tag), count($check_tag)]);
				}
				
				
				$atlas_tag_arr = $atlas_tag[0];
				if (count($atlas_tag) > 1) {
					foreach ($atlas_tag as $label) {
						$atlas_tag_arr = array_intersect($atlas_tag_arr, $label);
					}
				}
				$atlas_tag = [];
				foreach ($atlas_tag_arr as $item) {
					$atlas_tag[] = [
						'atlas_id' => $atlas_id,
						'label' => $item,
						'status' => 'normal'
					];
				}
				
				// 如果这是一个新的图集那就加上标签
				if (!$default_atlas_id) {
					AtlasTag::insert($atlas_tag);
				}
				
				// 本次合格的话自增并且把新的封面作为默认
				if ($total) {
					Atlas::where('id', '=', $atlas_id)->update([
						'picture_id' => $atlas_picture['id'],
						'total' => Db::raw('total + ' . $total)
					]);
					
				}
				// 存到关系表内
				AtlasList::insert($atlas_list);
				$task->save(['status' => 'finish']);
				
				return true;
			} catch (QueryException|\Exception $exception) {
				print_r(['数据处理发生错误']);
				print_r($exception);
				Context::set('error_msg', $exception->getMessage());
				if ($task_id) {
					DrawingTask::where('id', $task_id)->limit(1)->update(['status' => 'fail']);
				}
				return false;
			}
		}
	}